|
1 | 1 | import json
|
2 | 2 | from datetime import datetime
|
| 3 | +from unittest.mock import patch |
3 | 4 |
|
4 | 5 | from django.conf import settings
|
5 | 6 | from django.contrib.auth.models import User
|
| 7 | +from django.db import DatabaseError |
6 | 8 | from waffle.testutils import override_switch
|
7 | 9 |
|
8 | 10 | from kitsune.messages.utils import send_message
|
|
15 | 17 | process_event_subscription_state_change,
|
16 | 18 | )
|
17 | 19 | from kitsune.users.tests import AccountEventFactory, GroupFactory, ProfileFactory, UserFactory
|
| 20 | +from kitsune.wiki.tests import ApprovedRevisionFactory |
18 | 21 |
|
19 | 22 |
|
20 | 23 | class AccountEventsTasksTestCase(TestCase):
|
@@ -67,6 +70,39 @@ def test_process_delete_user(self):
|
67 | 70 |
|
68 | 71 | self.assertEqual(account_event.status, AccountEvent.PROCESSED)
|
69 | 72 |
|
| 73 | + @override_switch("enable-account-deletion", active=True) |
| 74 | + def test_process_delete_user_atomicity(self): |
| 75 | + """Ensure that the processing of the delete user event is atomic.""" |
| 76 | + profile = ProfileFactory() |
| 77 | + account_event = AccountEventFactory( |
| 78 | + body=json.dumps({}), |
| 79 | + event_type=AccountEvent.DELETE_USER, |
| 80 | + status=AccountEvent.UNPROCESSED, |
| 81 | + profile=profile, |
| 82 | + ) |
| 83 | + rev = ApprovedRevisionFactory(creator=profile.user) |
| 84 | + |
| 85 | + def event_save(*args, **kwargs): |
| 86 | + event_save.call_count += 1 |
| 87 | + if event_save.call_count > 1: |
| 88 | + raise DatabaseError() |
| 89 | + return super(AccountEvent, account_event).save(*args, **kwargs) |
| 90 | + |
| 91 | + event_save.call_count = 0 |
| 92 | + |
| 93 | + with patch("kitsune.users.tasks.AccountEvent.save") as event_save_mock: |
| 94 | + event_save_mock.side_effect = event_save |
| 95 | + with self.assertRaises(DatabaseError): |
| 96 | + process_event_delete_user(account_event.id) |
| 97 | + |
| 98 | + rev.refresh_from_db() |
| 99 | + account_event.refresh_from_db() |
| 100 | + |
| 101 | + self.assertEqual(account_event.profile, profile) |
| 102 | + self.assertEqual(account_event.status, AccountEvent.UNPROCESSED) |
| 103 | + self.assertEqual(rev.creator.username, profile.user.username) |
| 104 | + self.assertTrue(User.objects.filter(id=profile.user.id).exists()) |
| 105 | + |
70 | 106 | def test_process_subscription_state_change(self):
|
71 | 107 | product_1 = ProductFactory(codename="capability_1")
|
72 | 108 | product_2 = ProductFactory(codename="capability_2")
|
@@ -148,6 +184,36 @@ def test_process_subscription_state_change_out_of_order(self):
|
148 | 184 | account_event_3.refresh_from_db()
|
149 | 185 | self.assertEqual(account_event_3.status, AccountEvent.IGNORED)
|
150 | 186 |
|
| 187 | + def test_process_subscription_state_change_atomicity(self): |
| 188 | + """Ensure that the processing of the subscription state change is atomic.""" |
| 189 | + ProductFactory(codename="capability_1") |
| 190 | + ProductFactory(codename="capability_2") |
| 191 | + product = ProductFactory(codename="capability_3") |
| 192 | + profile = ProfileFactory() |
| 193 | + profile.products.add(product) |
| 194 | + account_event = AccountEventFactory( |
| 195 | + body=json.dumps( |
| 196 | + { |
| 197 | + "capabilities": ["capability_1", "capability_2"], |
| 198 | + "isActive": True, |
| 199 | + "changeTime": 1, |
| 200 | + } |
| 201 | + ), |
| 202 | + event_type=AccountEvent.SUBSCRIPTION_STATE_CHANGE, |
| 203 | + status=AccountEvent.UNPROCESSED, |
| 204 | + profile=profile, |
| 205 | + ) |
| 206 | + |
| 207 | + with patch("kitsune.users.tasks.AccountEvent.save") as event_save_mock: |
| 208 | + event_save_mock.side_effect = DatabaseError() |
| 209 | + with self.assertRaises(DatabaseError): |
| 210 | + process_event_subscription_state_change(account_event.id) |
| 211 | + |
| 212 | + account_event.refresh_from_db() |
| 213 | + |
| 214 | + self.assertEqual(account_event.status, AccountEvent.UNPROCESSED) |
| 215 | + self.assertEqual(list(p.codename for p in profile.products.all()), ["capability_3"]) |
| 216 | + |
151 | 217 | def test_process_password_change(self):
|
152 | 218 | profile = ProfileFactory()
|
153 | 219 | account_event_1 = AccountEventFactory(
|
@@ -179,3 +245,24 @@ def test_process_password_change(self):
|
179 | 245 |
|
180 | 246 | self.assertEqual(profile.fxa_password_change, datetime.utcfromtimestamp(2))
|
181 | 247 | self.assertEqual(account_event_2.status, AccountEvent.IGNORED)
|
| 248 | + |
| 249 | + def test_process_password_change_atomicity(self): |
| 250 | + """Ensure that the processing of the password change is atomic.""" |
| 251 | + profile = ProfileFactory() |
| 252 | + account_event = AccountEventFactory( |
| 253 | + body=json.dumps({"changeTime": 2000}), |
| 254 | + event_type=AccountEvent.PASSWORD_CHANGE, |
| 255 | + status=AccountEvent.UNPROCESSED, |
| 256 | + profile=profile, |
| 257 | + ) |
| 258 | + |
| 259 | + with patch("kitsune.users.tasks.AccountEvent.save") as event_save_mock: |
| 260 | + event_save_mock.side_effect = DatabaseError() |
| 261 | + with self.assertRaises(DatabaseError): |
| 262 | + process_event_password_change(account_event.id) |
| 263 | + |
| 264 | + profile.refresh_from_db() |
| 265 | + account_event.refresh_from_db() |
| 266 | + |
| 267 | + self.assertIs(profile.fxa_password_change, None) |
| 268 | + self.assertEqual(account_event.status, AccountEvent.UNPROCESSED) |
0 commit comments